package chapter08;

/**
 * @Author: insight
 * @Description: 泛型类的通配符
 * @LastDate: 2021/1/20
 */
public class Test08_WildCard {
    public static void main(String[] args) {
        //通配符的综合练习
        var kid1 = new Kid(2);
        var kid2 = new Kid(1);
        Kid[] kidA = {kid1, kid2};
        var kids = new Pair<Father>(kid1, kid2);

        printFamily(kids);

        var res = new Pair<Father>();
        minmax(kidA, res);
        System.out.println("first: " + res.getFirst().getAge() + ", second: " + res.getSecond().getAge());

        maxmin(kidA, res);
        System.out.println("first: " + res.getFirst().getAge() + ", second: " + res.getSecond().getAge());
    }

    /**
     * 无限定的通配符 功能介于子类限定和超类限定两者之间
     * getfirst可以调用 但是只能用Object接受
     * setfirst不能调用 甚至传入Object都不行
     *
     * 看起来Pair<?>和Pair无区别
     * 其实Pair可以用object调用setfirst
     * Pair<?>可以用于一些简单操作 如下
     * 也可以改写成<T> boolean hasNull(Pair<T> pair)
     * 但是无限定通配符 可读性更好
     */
    boolean hasNull(Pair<?> pair) {
        return pair.getFirst() == null;
    }

    public static void superWildcard() {
        /*
         * 超类型通配符转换
         * 可以写入
         */
        var fa = new Pair<Father>(new Father());
        //ok
        Pair<? super Kid> kid = fa;
        kid.setFirst(new Kid());

        /*
         * 超类型的一个应用 P351
         * 修改chapter08.Alg.getMin
         * <T extends Comparable> int getMin(T[] a) {}
         */
        //1. 修改Comparable
        // <T extends Comparable<T>> int getMin(T[] a)
        //2. 想让子类直接使用父类的comparable
        // <T extends Comparable<? super T>> int getMin(T[] a)
    }

    public static void whatIsWildcard() {
        /*
         * 假设我们有一个打印father信息的方法
         * 但是由于泛型类的继承规则 不能把kid传递进去打印
         * 但事实上我们想实现这种传递
         * 通配符就是为此而生 一个问号 ? extends Father
         * 此时 只要是Father的子类 全都可以继承Father的泛型类
         * 这种称为子类型限定 与之对应的是超类型限定
         * 与限定对应的是无限定通配符
         * Pair<? extends Father> 是 Pair<Kid>父类
         */

        /*
         * 子类型通配符转换
         * 不会破坏子类
         */
        var kid2 = new Pair<Kid>(new Kid());
        //ok
        Pair<? extends Father> wildcard = kid2;
        /*
         * 调用setfirst出错
         * 编译器只知道需要一个Father的某个子类型 但是不确定是哪个
         * 因此拒绝任何确定的类型 这就保证不会破坏引用了
         * getfirst就不会存在这种问题
         * set是确定->不确定 get是把不确定->确定
         */
        // wildcard.setFirst(new Father());
    }

    static void printFamily(Pair<? extends Father> pair) {
        Father t1 = pair.getFirst();
        Father t2 = pair.getSecond();
        System.out.println(t1.getAge() + " and " + t2.getAge());
    }

    static void minmax(Kid[] a, Pair<? super Kid> res) {
        if (a.length == 0) return;

        Kid min = a[0];
        Kid max = a[1];
        for (int i = 0; i < a.length; i++) {
           if (min.getAge() > a[i].getAge()) min = a[i];
           if (max.getAge() < a[i].getAge()) max = a[i];
        }
        res.setFirst(min);
        res.setSecond(max);
    }

    static void maxmin(Kid[] a, Pair<? super Kid> res) {
        minmax(a, res);
        PairAlg.swap(res);
    }
}

class PairAlg {
    static boolean hasNull(Pair<?> pair) {
        return pair.getFirst() == null || pair.getSecond() == null;
    }

    /**
     * 通配符捕获的应用 利用类型变量确定通配符
     */
    static void swap(Pair<?> pair) {
        swapHelper(pair);
    }

    static <T> void swapHelper(Pair<T> pair) {
        T t = pair.getFirst();
        pair.setFirst(pair.getSecond());
        pair.setSecond(t);
    }
}
